msg_tool\scripts\circus\image/
crx.rs

1//! Circus Image File (.crx)
2use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::img::*;
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use clap::ValueEnum;
9use clap::builder::PossibleValue;
10use msg_tool_macro::*;
11use overf::wrapping;
12use std::io::{Read, Seek, Write};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15/// Circus CRX Row Encoding Mode
16pub enum CircusCrxMode {
17    /// Encoding all rows with a fixed type.
18    Fixed(u8),
19    /// When importing, use origin mode; when creating, use best mode.
20    Auto,
21    /// Use origin mode for importing; when creating, fallback to best mode.
22    Origin,
23    /// Try to use the best mode for encoding.
24    Best,
25}
26
27impl Default for CircusCrxMode {
28    fn default() -> Self {
29        CircusCrxMode::Auto
30    }
31}
32
33impl CircusCrxMode {
34    /// Returns mode for importing.
35    pub fn for_importing(&self) -> Self {
36        match self {
37            CircusCrxMode::Auto => CircusCrxMode::Origin,
38            _ => *self,
39        }
40    }
41
42    /// Returns mode for creating.
43    pub fn for_creating(&self) -> Self {
44        match self {
45            CircusCrxMode::Auto => CircusCrxMode::Best,
46            CircusCrxMode::Origin => CircusCrxMode::Best,
47            _ => *self,
48        }
49    }
50
51    /// Checks if the mode is best.
52    pub fn is_best(&self) -> bool {
53        matches!(self, CircusCrxMode::Best)
54    }
55
56    /// Checks if the mode is origin.
57    pub fn is_origin(&self) -> bool {
58        matches!(self, CircusCrxMode::Origin)
59    }
60}
61
62impl ValueEnum for CircusCrxMode {
63    fn value_variants<'a>() -> &'a [Self] {
64        &[
65            CircusCrxMode::Fixed(0),
66            CircusCrxMode::Fixed(1),
67            CircusCrxMode::Fixed(2),
68            CircusCrxMode::Fixed(3),
69            CircusCrxMode::Fixed(4),
70            CircusCrxMode::Auto,
71            CircusCrxMode::Origin,
72            CircusCrxMode::Best,
73        ]
74    }
75
76    fn to_possible_value(&self) -> Option<PossibleValue> {
77        Some(match self {
78            CircusCrxMode::Fixed(0) => PossibleValue::new("0").help("Row type 0"),
79            CircusCrxMode::Fixed(1) => PossibleValue::new("1").help("Row type 1"),
80            CircusCrxMode::Fixed(2) => PossibleValue::new("2").help("Row type 2"),
81            CircusCrxMode::Fixed(3) => PossibleValue::new("3").help("Row type 3"),
82            CircusCrxMode::Fixed(4) => PossibleValue::new("4").help("Row type 4"),
83            CircusCrxMode::Auto => PossibleValue::new("auto")
84                .help("When importing, use origin mode, otherwise use best mode."),
85            CircusCrxMode::Origin => PossibleValue::new("origin")
86                .help("Use origin mode for importing. When creating, fallback to best mode."),
87            CircusCrxMode::Best => PossibleValue::new("best").help("Try to use the best mode."),
88            _ => return None,
89        })
90    }
91}
92
93#[derive(Debug)]
94/// Circus CRX Image Builder
95pub struct CrxImageBuilder {}
96
97impl CrxImageBuilder {
98    /// Creates a new instance of `CrxImageBuilder`.
99    pub const fn new() -> Self {
100        CrxImageBuilder {}
101    }
102}
103
104impl ScriptBuilder for CrxImageBuilder {
105    fn default_encoding(&self) -> Encoding {
106        Encoding::Cp932
107    }
108
109    fn build_script(
110        &self,
111        data: Vec<u8>,
112        _filename: &str,
113        _encoding: Encoding,
114        _archive_encoding: Encoding,
115        config: &ExtraConfig,
116        _archive: Option<&Box<dyn Script>>,
117    ) -> Result<Box<dyn Script>> {
118        Ok(Box::new(CrxImage::new(MemReader::new(data), config)?))
119    }
120
121    fn extensions(&self) -> &'static [&'static str] {
122        &["crx"]
123    }
124
125    fn script_type(&self) -> &'static ScriptType {
126        &ScriptType::CircusCrx
127    }
128
129    fn is_image(&self) -> bool {
130        true
131    }
132
133    fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
134        if buf_len >= 4 && buf.starts_with(b"CRXG") {
135            return Some(255);
136        }
137        None
138    }
139
140    fn can_create_image_file(&self) -> bool {
141        true
142    }
143
144    fn create_image_file<'a>(
145        &'a self,
146        data: ImageData,
147        writer: Box<dyn WriteSeek + 'a>,
148        options: &ExtraConfig,
149    ) -> Result<()> {
150        CrxImage::create_image(data, writer, options)
151    }
152}
153
154#[derive(Clone, Debug, StructPack, StructUnpack)]
155struct Clip {
156    field_0: u32,
157    img_width: u16,
158    img_height: u16,
159    clip_offset_x: u16,
160    clip_offset_y: u16,
161    clip_width: u16,
162    clip_height: u16,
163}
164
165#[derive(Clone, Debug, StructPack, StructUnpack)]
166struct Header {
167    inner_x: u16,
168    inner_y: u16,
169    width: u16,
170    height: u16,
171    version: u16,
172    flags: u16,
173    bpp: u16,
174    mode: u16,
175    #[skip_pack_if(self.version != 3)]
176    #[skip_unpack_if(version != 3)]
177    #[pvec(u32)]
178    clips: Vec<Clip>,
179}
180
181/// Circus CRX Image
182pub struct CrxImage {
183    header: Header,
184    color_type: ImageColorType,
185    data: Vec<u8>,
186    compress_level: u32,
187    keep_original_bpp: bool,
188    zstd: bool,
189    zstd_compression_level: i32,
190    row_type: CircusCrxMode,
191    canvas: bool,
192}
193
194impl std::fmt::Debug for CrxImage {
195    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196        f.debug_struct("CrxImage")
197            .field("header", &self.header)
198            .field("color_type", &self.color_type)
199            .field("data_length", &self.data.len())
200            .finish()
201    }
202}
203
204impl CrxImage {
205    /// Creates a new `CrxImage` from the given data and configuration.
206    ///
207    /// * `data` - The reader to read the CRX image from.
208    /// * `config` - Extra configuration options.
209    pub fn new<T: Read + Seek>(data: T, config: &ExtraConfig) -> Result<Self> {
210        let mut reader = data;
211        let mut magic = [0; 4];
212        reader.read_exact(&mut magic)?;
213        if magic != *b"CRXG" {
214            return Err(anyhow::anyhow!("Invalid CRX image magic"));
215        }
216        let header: Header = reader.read_struct(false, Encoding::Utf8)?;
217        if header.version < 2 || header.version > 3 {
218            return Err(anyhow::anyhow!(
219                "Unsupported CRX version: {}",
220                header.version
221            ));
222        }
223        let color_type = if header.bpp == 0 {
224            ImageColorType::Bgr
225        } else if header.bpp == 1 {
226            ImageColorType::Bgra
227        } else {
228            return Err(anyhow::anyhow!("Unsupported CRX bpp: {}", header.bpp));
229        };
230        let compressed_size = if (header.flags & 0x10) == 0 {
231            let len = reader.stream_length()?;
232            (len - reader.stream_position()?) as u32
233        } else {
234            reader.read_u32()?
235        };
236        let compressed_data = reader.read_exact_vec(compressed_size as usize)?;
237        let uncompessed = if compressed_data.starts_with(&[0x28, 0xb5, 0x2f, 0xfd]) {
238            let mut decoder = zstd::Decoder::new(MemReaderRef::new(&compressed_data))?;
239            let mut decompressed_data = Vec::new();
240            decoder.read_to_end(&mut decompressed_data)?;
241            decompressed_data
242        } else {
243            let mut decompressed_data = Vec::new();
244            flate2::read::ZlibDecoder::new(MemReaderRef::new(&compressed_data))
245                .read_to_end(&mut decompressed_data)?;
246            decompressed_data
247        };
248        Ok(CrxImage {
249            header,
250            color_type,
251            data: uncompessed,
252            compress_level: config.zlib_compression_level,
253            keep_original_bpp: config.circus_crx_keep_original_bpp,
254            zstd: config.circus_crx_zstd,
255            zstd_compression_level: config.zstd_compression_level,
256            row_type: config.circus_crx_mode.for_importing(),
257            canvas: config.circus_crx_canvas,
258        })
259    }
260
261    /// Whether to draw image on canvas if canvas's width and height are specified in image.
262    pub fn with_canvas(mut self, canvas: bool) -> Self {
263        self.canvas = canvas;
264        self
265    }
266
267    /// Draws another image on this image.
268    ///
269    /// Returns a new `ImageData` with the combined image.
270    pub fn draw_diff(&self, diff: &Self) -> Result<ImageData> {
271        let base_header = &self.header;
272        let diff_header = &diff.header;
273        let (img_width, img_height) =
274            if base_header.clips.is_empty() && diff_header.clips.is_empty() {
275                (
276                    (base_header.width + base_header.inner_x)
277                        .max(diff_header.width + diff_header.inner_x),
278                    (base_header.height + base_header.inner_y)
279                        .max(diff_header.height + diff_header.inner_y),
280                )
281            } else {
282                if base_header.clips.is_empty() {
283                    let clip = &diff_header.clips[0];
284                    (clip.img_width, clip.img_height)
285                } else {
286                    let clip = &base_header.clips[0];
287                    (clip.img_width, clip.img_height)
288                }
289            };
290        let base = self.export_image()?;
291        let mut nw = draw_on_canvas(
292            base,
293            img_width as u32,
294            img_height as u32,
295            base_header.inner_x as u32,
296            base_header.inner_y as u32,
297        )?;
298        draw_on_img(
299            &mut nw,
300            &diff.export_image()?,
301            diff_header.inner_x as u32,
302            diff_header.inner_y as u32,
303        )?;
304        Ok(nw)
305    }
306
307    fn decode_row0(
308        dst: &mut Vec<u8>,
309        mut dst_p: usize,
310        src: &[u8],
311        mut src_p: usize,
312        width: u16,
313        pixel_size: u8,
314    ) -> Result<usize> {
315        let mut prev_p = dst_p;
316        for _ in 0..pixel_size {
317            dst[dst_p] = src[src_p];
318            dst_p += 1;
319            src_p += 1;
320        }
321        let remaining = width - 1;
322        for _ in 0..remaining {
323            for _ in 0..pixel_size {
324                dst[dst_p] = src[src_p].overflowing_add(dst[prev_p]).0;
325                dst_p += 1;
326                src_p += 1;
327                prev_p += 1;
328            }
329        }
330        Ok(src_p)
331    }
332
333    fn decode_row1(
334        dst: &mut Vec<u8>,
335        mut dst_p: usize,
336        src: &[u8],
337        mut src_p: usize,
338        width: u16,
339        pixel_size: u8,
340        mut prev_row_p: usize,
341    ) -> Result<usize> {
342        for _ in 0..width {
343            for _ in 0..pixel_size {
344                dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
345                dst_p += 1;
346                src_p += 1;
347                prev_row_p += 1;
348            }
349        }
350        Ok(src_p)
351    }
352
353    fn decode_row2(
354        dst: &mut Vec<u8>,
355        mut dst_p: usize,
356        src: &[u8],
357        mut src_p: usize,
358        width: u16,
359        pixel_size: u8,
360        mut prev_row_p: usize,
361    ) -> Result<usize> {
362        for _ in 0..pixel_size {
363            dst[dst_p] = src[src_p];
364            dst_p += 1;
365            src_p += 1;
366        }
367        let remaining = width - 1;
368        for _ in 0..remaining {
369            for _ in 0..pixel_size {
370                dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
371                dst_p += 1;
372                src_p += 1;
373                prev_row_p += 1;
374            }
375        }
376        Ok(src_p)
377    }
378
379    fn decode_row3(
380        dst: &mut Vec<u8>,
381        mut dst_p: usize,
382        src: &[u8],
383        mut src_p: usize,
384        width: u16,
385        pixel_size: u8,
386        mut prev_row_p: usize,
387    ) -> Result<usize> {
388        let count = width - 1;
389        prev_row_p += pixel_size as usize;
390        for _ in 0..count {
391            for _ in 0..pixel_size {
392                dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
393                dst_p += 1;
394                src_p += 1;
395                prev_row_p += 1;
396            }
397        }
398        for _ in 0..pixel_size {
399            dst[dst_p] = src[src_p];
400            dst_p += 1;
401            src_p += 1;
402        }
403        Ok(src_p)
404    }
405
406    fn decode_row4(
407        dst: &mut Vec<u8>,
408        dst_p: usize,
409        src: &[u8],
410        mut src_p: usize,
411        width: u16,
412        pixel_size: u8,
413    ) -> Result<usize> {
414        for offset in 0..pixel_size {
415            let mut dst_c = dst_p + offset as usize;
416            let mut remaining = width;
417            let value = src[src_p];
418            src_p += 1;
419            dst[dst_c] = value;
420            dst_c += pixel_size as usize;
421            remaining -= 1;
422            if remaining == 0 {
423                continue;
424            }
425            if value == src[src_p] {
426                src_p += 1;
427                let count = src[src_p] as u16;
428                src_p += 1;
429                remaining -= count;
430                for _ in 0..count {
431                    dst[dst_c] = value;
432                    dst_c += pixel_size as usize;
433                }
434            }
435            while remaining > 0 {
436                let value = src[src_p];
437                src_p += 1;
438                dst[dst_c] = value;
439                dst_c += pixel_size as usize;
440                remaining -= 1;
441                if remaining == 0 {
442                    break;
443                }
444                if value == src[src_p] {
445                    src_p += 1;
446                    let count = src[src_p] as u16;
447                    src_p += 1;
448                    remaining -= count;
449                    for _ in 0..count {
450                        dst[dst_c] = value;
451                        dst_c += pixel_size as usize;
452                    }
453                }
454            }
455        }
456        Ok(src_p)
457    }
458
459    fn decode_image(
460        dst: &mut Vec<u8>,
461        src: &[u8],
462        width: u16,
463        height: u16,
464        pixel_size: u8,
465        encode_type: &mut Vec<u8>,
466    ) -> Result<()> {
467        let mut src_p = 0;
468        let mut dst_p = 0;
469        let mut prev_row_p = 0;
470        for _ in 0..height {
471            let data = src[src_p];
472            encode_type.push(data);
473            src_p += 1;
474            match data {
475                0 => {
476                    src_p = Self::decode_row0(dst, dst_p, src, src_p, width, pixel_size)?;
477                }
478                1 => {
479                    src_p =
480                        Self::decode_row1(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
481                }
482                2 => {
483                    src_p =
484                        Self::decode_row2(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
485                }
486                3 => {
487                    src_p =
488                        Self::decode_row3(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
489                }
490                4 => {
491                    src_p = Self::decode_row4(dst, dst_p, src, src_p, width, pixel_size)?;
492                }
493                _ => {
494                    return Err(anyhow::anyhow!("Invalid row type: {}", data));
495                }
496            }
497            prev_row_p = dst_p;
498            dst_p += pixel_size as usize * width as usize;
499        }
500        Ok(())
501    }
502
503    fn encode_row0(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
504        let pixel_size = pixel_size as usize;
505        let mut src_p = y as usize * width as usize * pixel_size;
506        for _ in 0..pixel_size {
507            dst.push(src[src_p]);
508            src_p += 1;
509        }
510        for _ in 1..width {
511            for _ in 0..pixel_size {
512                dst.push(src[src_p].wrapping_sub(src[src_p - pixel_size]));
513                src_p += 1;
514            }
515        }
516    }
517
518    fn encode_row1(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
519        let pixel_size = pixel_size as usize;
520        let mut src_p = y as usize * width as usize * pixel_size;
521        let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size;
522        for _ in 0..width {
523            for _ in 0..pixel_size {
524                dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
525                src_p += 1;
526                prev_row_p += 1;
527            }
528        }
529    }
530
531    fn encode_row2(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
532        let pixel_size = pixel_size as usize;
533        let mut src_p = y as usize * width as usize * pixel_size;
534        let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size;
535        for _ in 0..pixel_size {
536            dst.push(src[src_p]);
537            src_p += 1;
538        }
539        for _ in 1..width {
540            for _ in 0..pixel_size {
541                dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
542                src_p += 1;
543                prev_row_p += 1;
544            }
545        }
546    }
547
548    fn encode_row3(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
549        let pixel_size = pixel_size as usize;
550        let mut src_p = y as usize * width as usize * pixel_size;
551        let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size + pixel_size;
552        for _ in 0..width - 1 {
553            for _ in 0..pixel_size {
554                dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
555                src_p += 1;
556                prev_row_p += 1;
557            }
558        }
559        for _ in 0..pixel_size {
560            dst.push(src[src_p]);
561            src_p += 1;
562        }
563    }
564
565    fn encode_row4(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
566        let pixel_size = pixel_size as usize;
567        let src_p = y as usize * width as usize * pixel_size;
568        for offset in 0..pixel_size {
569            let mut src_c = src_p + offset;
570            let mut remaining = width;
571            let value = src[src_c];
572            src_c += pixel_size;
573            dst.push(value);
574            remaining -= 1;
575            if remaining == 0 {
576                continue;
577            }
578            let mut count = 0;
579            loop {
580                if count as u16 >= remaining || count >= 255 || src[src_c] != value {
581                    break;
582                }
583                src_c += pixel_size;
584                count += 1;
585            }
586            if count > 0 {
587                dst.push(value);
588                dst.push(count);
589                remaining -= count as u16;
590            }
591            while remaining > 0 {
592                let value = src[src_c];
593                src_c += pixel_size;
594                dst.push(value);
595                remaining -= 1;
596                if remaining == 0 {
597                    break;
598                }
599                let mut count = 0;
600                loop {
601                    if count as u16 >= remaining || count >= 255 || src[src_c] != value {
602                        break;
603                    }
604                    src_c += pixel_size;
605                    count += 1;
606                }
607                if count > 0 {
608                    dst.push(value);
609                    dst.push(count);
610                    remaining -= count as u16;
611                }
612            }
613        }
614    }
615
616    fn encode_row_best(
617        dst: &mut Vec<u8>,
618        src: &[u8],
619        width: u16,
620        pixel_size: u8,
621        y: u16,
622    ) -> Result<()> {
623        let mut buf = Vec::with_capacity(width as usize * pixel_size as usize);
624        Self::encode_row0(&mut buf, src, width, pixel_size, y);
625        let mut compressed_len = {
626            let mut encoder =
627                flate2::write::ZlibEncoder::new(MemWriter::new(), flate2::Compression::fast());
628            encoder.write_all(&buf)?;
629            let compressed_data = encoder.finish()?;
630            compressed_data.into_inner().len()
631        };
632        let mut buf_row_type = 0;
633        for row_type in 1..5u8 {
634            if y == 0 && row_type < 4 {
635                continue;
636            }
637            let mut newbuf = Vec::with_capacity(width as usize * pixel_size as usize);
638            match row_type {
639                1 => Self::encode_row1(&mut newbuf, src, width, pixel_size, y),
640                2 => Self::encode_row2(&mut newbuf, src, width, pixel_size, y),
641                3 => Self::encode_row3(&mut newbuf, src, width, pixel_size, y),
642                4 => Self::encode_row4(&mut newbuf, src, width, pixel_size, y),
643                _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
644            };
645            let new_compressed_len = {
646                let mut encoder =
647                    flate2::write::ZlibEncoder::new(MemWriter::new(), flate2::Compression::fast());
648                encoder.write_all(&newbuf)?;
649                let compressed_data = encoder.finish()?;
650                compressed_data.into_inner().len()
651            };
652            if new_compressed_len < compressed_len {
653                compressed_len = new_compressed_len;
654                buf = newbuf;
655                buf_row_type = row_type;
656            }
657        }
658        dst.push(buf_row_type);
659        dst.extend_from_slice(&buf);
660        Ok(())
661    }
662
663    fn encode_image_best(src: &[u8], width: u16, height: u16, pixel_size: u8) -> Result<Vec<u8>> {
664        let size = width as usize * height as usize * pixel_size as usize + height as usize;
665        let mut dst = Vec::with_capacity(size);
666        for y in 0..height {
667            Self::encode_row_best(&mut dst, src, width, pixel_size, y)?;
668        }
669        Ok(dst)
670    }
671
672    fn encode_image_fixed(
673        src: &[u8],
674        width: u16,
675        height: u16,
676        pixel_size: u8,
677        row_type: u8,
678    ) -> Result<Vec<u8>> {
679        let size = width as usize * height as usize * pixel_size as usize + height as usize;
680        let mut dst = Vec::with_capacity(size);
681        for y in 0..height {
682            let row_type = if y == 0 && row_type != 0 && row_type != 4 {
683                0
684            } else {
685                row_type
686            };
687            dst.push(row_type);
688            match row_type {
689                0 => Self::encode_row0(&mut dst, src, width, pixel_size, y),
690                1 => Self::encode_row1(&mut dst, src, width, pixel_size, y),
691                2 => Self::encode_row2(&mut dst, src, width, pixel_size, y),
692                3 => Self::encode_row3(&mut dst, src, width, pixel_size, y),
693                4 => Self::encode_row4(&mut dst, src, width, pixel_size, y),
694                _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
695            };
696        }
697        Ok(dst)
698    }
699
700    fn encode_image_origin(
701        src: &[u8],
702        width: u16,
703        height: u16,
704        pixel_size: u8,
705        row_type: &[u8],
706    ) -> Result<Vec<u8>> {
707        if row_type.len() != height as usize {
708            return Err(anyhow::anyhow!("Row type length does not match height"));
709        }
710        let size = width as usize * height as usize * pixel_size as usize + height as usize;
711        let mut dst = Vec::with_capacity(size);
712        for y in 0..height {
713            let row_type = row_type[y as usize];
714            dst.push(row_type);
715            match row_type {
716                0 => Self::encode_row0(&mut dst, src, width, pixel_size, y),
717                1 => Self::encode_row1(&mut dst, src, width, pixel_size, y),
718                2 => Self::encode_row2(&mut dst, src, width, pixel_size, y),
719                3 => Self::encode_row3(&mut dst, src, width, pixel_size, y),
720                4 => Self::encode_row4(&mut dst, src, width, pixel_size, y),
721                _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
722            };
723        }
724        Ok(dst)
725    }
726
727    /// Creates a CRX image file from the given image data and writes it to the specified writer.
728    ///
729    /// * `data` - The input image data.
730    /// * `writer` - The writer to write the CRX image to.
731    /// * `config` - Extra configuration options.
732    pub fn create_image<T: Write + Seek>(
733        mut data: ImageData,
734        mut writer: T,
735        config: &ExtraConfig,
736    ) -> Result<()> {
737        let header = Header {
738            inner_x: 0,
739            inner_y: 0,
740            width: data.width as u16,
741            height: data.height as u16,
742            version: 2,
743            flags: 0x10, // Force add compressed data length
744            bpp: match data.color_type {
745                ImageColorType::Bgr => 0,
746                ImageColorType::Bgra => 1,
747                ImageColorType::Rgb => {
748                    convert_rgb_to_bgr(&mut data)?;
749                    0
750                }
751                ImageColorType::Rgba => {
752                    convert_rgba_to_bgra(&mut data)?;
753                    1
754                }
755                _ => {
756                    return Err(anyhow::anyhow!(
757                        "Unsupported color type: {:?}",
758                        data.color_type
759                    ));
760                }
761            },
762            mode: 0,
763            clips: Vec::new(),
764        };
765        let pixel_size = data.color_type.bpp(1) as u8;
766        if data.color_type == ImageColorType::Bgra && header.mode != 1 {
767            let alpha_flip = if header.mode == 2 { 0 } else { 0xFF };
768            for i in (0..data.data.len()).step_by(4) {
769                let b = data.data[i];
770                let g = data.data[i + 1];
771                let r = data.data[i + 2];
772                let a = data.data[i + 3];
773                data.data[i] = a ^ alpha_flip;
774                data.data[i + 1] = b;
775                data.data[i + 2] = g;
776                data.data[i + 3] = r;
777            }
778        }
779        let mode = config.circus_crx_mode.for_creating();
780        let encoded = if mode.is_best() {
781            Self::encode_image_best(&data.data, header.width, header.height, pixel_size)?
782        } else if let CircusCrxMode::Fixed(mode) = mode {
783            Self::encode_image_fixed(&data.data, header.width, header.height, pixel_size, mode)?
784        } else {
785            return Err(anyhow::anyhow!(
786                "Unsupported row type for creating: {:?}",
787                mode
788            ));
789        };
790        let compressed = if config.circus_crx_zstd {
791            let mut encoder = zstd::Encoder::new(MemWriter::new(), config.zstd_compression_level)?;
792            encoder.write_all(&encoded)?;
793            let compressed_data = encoder.finish()?;
794            compressed_data.into_inner()
795        } else {
796            let mut encoder = flate2::write::ZlibEncoder::new(
797                MemWriter::new(),
798                flate2::Compression::new(config.zlib_compression_level),
799            );
800            encoder.write_all(&encoded)?;
801            let compressed_data = encoder.finish()?;
802            compressed_data.into_inner()
803        };
804        writer.write_all(b"CRXG")?;
805        header.pack(&mut writer, false, Encoding::Utf8)?;
806        writer.write_u32(compressed.len() as u32)?;
807        writer.write_all(&compressed)?;
808        Ok(())
809    }
810}
811
812impl Script for CrxImage {
813    fn default_output_script_type(&self) -> OutputScriptType {
814        OutputScriptType::Json
815    }
816
817    fn default_format_type(&self) -> FormatOptions {
818        FormatOptions::None
819    }
820
821    fn is_image(&self) -> bool {
822        true
823    }
824
825    fn export_image(&self) -> Result<ImageData> {
826        let data_size = self.color_type.bpp(1) as usize
827            * self.header.width as usize
828            * self.header.height as usize;
829        let mut data = vec![0; data_size];
830        let mut encode_type = Vec::new();
831        Self::decode_image(
832            &mut data,
833            &self.data,
834            self.header.width,
835            self.header.height,
836            self.color_type.bpp(1) as u8,
837            &mut encode_type,
838        )?;
839        if self.color_type.bpp(1) == 4 && self.header.mode != 1 {
840            let alpha_flip = if self.header.mode == 2 { 0 } else { 0xFF };
841            for i in (0..data_size).step_by(4) {
842                let a = data[i];
843                let b = data[i + 1];
844                let g = data[i + 2];
845                let r = data[i + 3];
846                data[i] = b;
847                data[i + 1] = g;
848                data[i + 2] = r;
849                data[i + 3] = a ^ alpha_flip;
850            }
851        }
852        let img = ImageData {
853            width: self.header.width as u32,
854            height: self.header.height as u32,
855            depth: 8,
856            color_type: self.color_type,
857            data,
858        };
859        if self.canvas {
860            let (img_width, img_height) = if self.header.clips.is_empty() {
861                (self.header.width as u32, self.header.height as u32)
862            } else {
863                let clip = &self.header.clips[0];
864                (clip.img_width as u32, clip.img_height as u32)
865            };
866            return Ok(draw_on_canvas(
867                img,
868                img_width,
869                img_height,
870                self.header.inner_x as u32,
871                self.header.inner_y as u32,
872            )?);
873        }
874        Ok(img)
875    }
876
877    fn import_image<'a>(
878        &'a self,
879        mut data: ImageData,
880        mut file: Box<dyn WriteSeek + 'a>,
881    ) -> Result<()> {
882        let mut color_type = match data.color_type {
883            ImageColorType::Bgr => ImageColorType::Bgr,
884            ImageColorType::Bgra => ImageColorType::Bgra,
885            ImageColorType::Rgb => {
886                convert_rgb_to_bgr(&mut data)?;
887                ImageColorType::Bgr
888            }
889            ImageColorType::Rgba => {
890                convert_rgba_to_bgra(&mut data)?;
891                ImageColorType::Bgra
892            }
893            _ => {
894                return Err(anyhow::anyhow!(
895                    "Unsupported color type: {:?}",
896                    data.color_type
897                ));
898            }
899        };
900        if data.width != self.header.width as u32 {
901            return Err(anyhow::anyhow!(
902                "Image width does not match: expected {}, got {}",
903                self.header.width,
904                data.width
905            ));
906        }
907        if data.height != self.header.height as u32 {
908            return Err(anyhow::anyhow!(
909                "Image height does not match: expected {}, got {}",
910                self.header.height,
911                data.height
912            ));
913        }
914        if data.depth != 8 {
915            return Err(anyhow::anyhow!("Image depth must be 8, got {}", data.depth));
916        }
917        if data.color_type != self.color_type && self.keep_original_bpp {
918            if self.color_type == ImageColorType::Bgr {
919                convert_bgra_to_bgr(&mut data)?;
920            } else if self.color_type == ImageColorType::Bgra {
921                convert_bgr_to_bgra(&mut data)?;
922            } else {
923                return Err(anyhow::anyhow!(
924                    "Unsupported color type for import: {:?}",
925                    self.color_type
926                ));
927            }
928            color_type = self.color_type;
929        }
930        let mut new_header = self.header.clone();
931        new_header.bpp = match color_type {
932            ImageColorType::Bgr => 0,
933            ImageColorType::Bgra => 1,
934            _ => return Err(anyhow::anyhow!("Unsupported color type: {:?}", color_type)),
935        };
936        new_header.flags |= 0x10; // Force add compressed data length
937        let pixel_size = color_type.bpp(1) as u8;
938        if color_type == ImageColorType::Bgra && self.header.mode != 1 {
939            let alpha_flip = if self.header.mode == 2 { 0 } else { 0xFF };
940            for i in (0..data.data.len()).step_by(4) {
941                let b = data.data[i];
942                let g = data.data[i + 1];
943                let r = data.data[i + 2];
944                let a = data.data[i + 3];
945                data.data[i] = a ^ alpha_flip;
946                data.data[i + 1] = b;
947                data.data[i + 2] = g;
948                data.data[i + 3] = r;
949            }
950        }
951        let encoded = if self.row_type.is_origin() {
952            let mut row_type = Vec::with_capacity(self.header.height as usize);
953            let row_len = self.header.width as usize * self.color_type.bpp(1) as usize + 1;
954            let mut cur_pos = 0;
955            for _ in 0..self.header.height {
956                row_type.push(self.data[cur_pos]);
957                cur_pos += row_len;
958            }
959            Self::encode_image_origin(
960                &data.data,
961                new_header.width,
962                new_header.height,
963                pixel_size,
964                &row_type,
965            )?
966        } else if self.row_type.is_best() {
967            Self::encode_image_best(&data.data, new_header.width, new_header.height, pixel_size)?
968        } else if let CircusCrxMode::Fixed(mode) = self.row_type {
969            Self::encode_image_fixed(
970                &data.data,
971                new_header.width,
972                new_header.height,
973                pixel_size,
974                mode,
975            )?
976        } else {
977            return Err(anyhow::anyhow!(
978                "Unsupported row type for import: {:?}",
979                self.row_type
980            ));
981        };
982        let compressed = if self.zstd {
983            let mut encoder = zstd::Encoder::new(MemWriter::new(), self.zstd_compression_level)?;
984            encoder.write_all(&encoded)?;
985            let compressed_data = encoder.finish()?;
986            compressed_data.into_inner()
987        } else {
988            let mut encoder = flate2::write::ZlibEncoder::new(
989                MemWriter::new(),
990                flate2::Compression::new(self.compress_level),
991            );
992            encoder.write_all(&encoded)?;
993            let compressed_data = encoder.finish()?;
994            compressed_data.into_inner()
995        };
996        file.write_all(b"CRXG")?;
997        new_header.pack(&mut file, false, Encoding::Utf8)?;
998        file.write_u32(compressed.len() as u32)?;
999        file.write_all(&compressed)?;
1000        Ok(())
1001    }
1002}
1003
1004fn draw_on_img(base: &mut ImageData, diff: &ImageData, left: u32, top: u32) -> Result<()> {
1005    if base.color_type != diff.color_type {
1006        return Err(anyhow::anyhow!(
1007            "Color types do not match: {:?} vs {:?}",
1008            base.color_type,
1009            diff.color_type
1010        ));
1011    }
1012    let bpp = base.color_type.bpp(1) as usize;
1013    let base_stride = base.width as usize * bpp;
1014    let diff_stride = diff.width as usize * bpp;
1015
1016    for y in 0..diff.height {
1017        let base_y = top + y;
1018        if base_y >= base.height {
1019            continue; // Skip if the base image is not tall enough
1020        }
1021
1022        for x in 0..diff.width {
1023            let base_x = left + x;
1024            if base_x >= base.width {
1025                continue; // Skip if the base image is not wide enough
1026            }
1027
1028            let base_index = (base_y as usize * base_stride) + (base_x as usize * bpp);
1029            let diff_index = (y as usize * diff_stride) + (x as usize * bpp);
1030
1031            let diff_pixel = &diff.data[diff_index..diff_index + bpp];
1032            let base_pixel_orig = base.data[base_index..base_index + bpp].to_vec();
1033            let mut b = base_pixel_orig[0];
1034            let mut g = base_pixel_orig[1];
1035            let mut r = base_pixel_orig[2];
1036            wrapping! {
1037                b += diff_pixel[0];
1038                g += diff_pixel[1];
1039                r += diff_pixel[2];
1040            }
1041            base.data[base_index] = b;
1042            base.data[base_index + 1] = g;
1043            base.data[base_index + 2] = r;
1044            if bpp == 4 {
1045                let mut a = base_pixel_orig[3];
1046                wrapping! {
1047                    a -= diff_pixel[3];
1048                }
1049                base.data[base_index + 3] = a;
1050            }
1051        }
1052    }
1053    Ok(())
1054}